﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Diagnostics;

namespace LowLevelGraphics.Extractors
{
    /// <summary>
    /// This will create a cooccurrence matrix from a graylevel pic
    /// Relations (Points) are relative coordinate values in here. 
    /// 
    /// If a relation could be something like GrayValue(x,y) > RelatedGrayValue(x+1,Y)
    /// this has to be refactured into a class. 
    /// </summary>
    /// <remarks>If one</remarks>
    public class CoocurrenceMatrix
    {
        protected int[,] m_aMatrixKernel = null;
        protected List<Point> m_aRelation = new List<Point>() { new Point(1, 0) }; //This is the start relation
        protected UnsafeBitmap m_GrayKernelBitmap = null;

        /// <summary>
        /// Initializes a new instance of the <see cref="CoocurrenceMatrix"/> class.
        /// </summary>
        public CoocurrenceMatrix()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CoocurrenceMatrix"/> class.
        /// </summary>
        /// <param name="_ptPoint">The _PT point.</param>
        public CoocurrenceMatrix(Point _ptPoint)
            : this(new Point[]{_ptPoint})
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CoocurrenceMatrix"/> class.
        /// </summary>
        /// <param name="_aPoint">The _a point.</param>
        public CoocurrenceMatrix(IEnumerable<Point> _aPoint)
        {
            this.m_aRelation.Clear();
            foreach (Point ptPoint in _aPoint)
            {
                this.m_aRelation.Add(ptPoint);
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CoocurrenceMatrix"/> class.
        /// </summary>
        /// <param name="_coocurrentMatrix">The _coocurrent matrix.</param>
        internal CoocurrenceMatrix(CoocurrenceMatrix _coocurrentMatrix)
        {
            this.m_aMatrixKernel = _coocurrentMatrix.m_aMatrixKernel;
            this.m_aRelation = _coocurrentMatrix.m_aRelation;
        }

        /// <summary>
        /// Gets or sets the relations.
        /// </summary>
        /// <value>The relations.</value>
        public List<Point> Relations
        {
            get { return m_aRelation; }
            set { m_aRelation = value; }
        }

        /// <summary>
        /// Gets the matrix kernel.
        /// </summary>
        /// <value>The matrix kernel.</value>
        public int[,] MatrixKernel
        {
            get { return m_aMatrixKernel; }
        }
        /// <summary>
        /// Extracts the specified _bitmap.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        public int[,] Extract(UnsafeBitmap _bitmap)
        {
            int nHeight = _bitmap.Height;
            int nWidth = _bitmap.Width;
            int y=0;
            int x=0;

            int nGray = -1;
            int nGrayRelated = -1;

            Dictionary<KeyValuePair<int, int>, int> aCooccurrence = new Dictionary<KeyValuePair<int, int>, int>();

            //Create Matrix here.
            int nTempX = 0;
            int nTempY = 0;

            for (y = 0; y < nHeight; y++)
            {
                for (x = 0; x < nWidth; x++)
                {
                    nGray = _bitmap[x, y, true].R; //read gray value
                    foreach (Point pt in Relations)
                    {
                        nTempX = x + pt.X;
                        nTempY = y + pt.Y;

                        //don't consider points that are out of reach
                        if (nTempX < 0 || nTempX > nWidth - 1 || nTempY < 0 || nTempY > nHeight - 1) continue;

                        //otherwise get it. 
                        nGrayRelated = _bitmap[nTempX, nTempY, true].R; //Get related gray Value

                        //Does this sequence really belong in here when several relations are considered? CHECK THIS
                        //For only one relation it is really correct.
                        if (!aCooccurrence.ContainsKey(new KeyValuePair<int, int>(nGray, nGrayRelated)))
                        {
                            aCooccurrence.Add(new KeyValuePair<int, int>(nGray, nGrayRelated), 1);
                        }
                        else
                        {
                            aCooccurrence[new KeyValuePair<int, int>(nGray, nGrayRelated)]++;
                        }
                    }
                }
            }

            Size size = GetMaxExtension(aCooccurrence);
            //TODO: determine size here...
            int nKernelWidth = size.Width;
            int nKernelHeight = size.Height;

            m_aMatrixKernel = new int[nKernelWidth,nKernelHeight];

            int nKernelBitmapWidth = nKernelWidth;
            int nKernelBitmapHeight = nKernelHeight;
            m_GrayKernelBitmap = new UnsafeBitmap(new Bitmap(nKernelBitmapWidth, nKernelBitmapHeight));
            foreach (KeyValuePair<KeyValuePair<int, int>, int> keyValuePair in aCooccurrence)
            {
                KeyValuePair<int, int> coordinates = keyValuePair.Key;
                int nX = coordinates.Key;
                int nY = coordinates.Value;
                int nCount = keyValuePair.Value;

                m_aMatrixKernel[nX, nY] = nCount;
                m_GrayKernelBitmap.SetGrayPixel(x, y, nCount);
                //generate also Gray image here
            }

            Debug.WriteLine(this.ToString());
            return m_aMatrixKernel;
        }

        /// <summary>
        /// Gets the extract gray image matrix kernel.
        /// </summary>
        /// <value>The extract gray image matrix kernel.</value>
        public UnsafeBitmap ExtractGrayImageMatrixKernel
        {
            get { return m_GrayKernelBitmap; }
        }

        /// <summary>
        /// Gets the kernel.
        /// </summary>
        /// <value>The kernel.</value>
        public int[,] Kernel
        {
            get { return m_aMatrixKernel; }
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        /// <remarks>To display this like defined in the kernel before we need to exchange x and y here. 
        /// Like done in m_aMatrixKernel[y,x]
        /// </remarks>
        public override string ToString()
        {
            string sText = string.Empty;
            for (int y = 0; y < m_aMatrixKernel.GetLongLength(0); y++)
            {
                string sLine = string.Empty;
                for (int x = 0; x < m_aMatrixKernel.GetLongLength(1); x++)
                {
                    sLine += m_aMatrixKernel[y, x].ToString()+" ";
                }
                sLine = sLine.TrimEnd(' ');
                sText += sLine + Environment.NewLine;
            }
            return sText;
        }

        /// <summary>
        /// Gets the max extension.
        /// </summary>
        /// <param name="_aValue">The _a value.</param>
        /// <returns></returns>
        public Size GetMaxExtension(Dictionary<KeyValuePair<int, int>, int> _aValue)
        {
            int nMaxX = -1;
            int nMaxY = -1;
            foreach (KeyValuePair<KeyValuePair<int, int>, int> keyValuePair in _aValue)
            {
                KeyValuePair<int, int> coordinates = keyValuePair.Key;

                int nX = coordinates.Key;
                if (nX > nMaxX) nMaxX = nX;

                int nY = coordinates.Value;
                if (nY > nMaxY) nMaxY = nY;
            }
            //Since we count from 0 on, we have to add 1 here, because we need the COUNT of the values. not the maxvalue itself
            return new Size(nMaxX + 1, nMaxY + 1);
        }
    }
}
